SuperStrict
Import pub.glew
'Import BRL.BMPLoader
Import BRL.JPGLoader

AppTitle = "Imageprocessing with openGL"
'for shapes:
Global quadObj:Byte Ptr = Null;

'Globals, consts:
Global lowLevelAvailable:Int = False;
Global highLevelAvailable:Int = False;
Global useHighLevel:Int = False;

Const PASS_THROUGH:Int=  0
Const BLUR:Int=          1
Const SHARPEN:Int=       2
Const DILATION:Int=      3
Const EROSION:Int=       4
Const LAPLACIAN:Int=     5
Const SOBEL:Int=         6
Const PREWITT:Int=       7
Const RADIALBLUR:Int=    8
Const TOTAL_SHADERS:Int=  9

Global ids:Int[TOTAL_SHADERS]
Global vShader:Int[TOTAL_SHADERS]
Global fShader:Int[TOTAL_SHADERS], progObj:Int[TOTAL_SHADERS] 'high level shader object handles
Global needsValidation:Int[TOTAL_SHADERS] 'if program obj changed, then revalidate
Global shaderNames:String[] = ["passthrough", "blur", "sharpen", "dilation", "erosion", "laplacian", "sobel", "prewitt", "radialBlur"]

Global whichShader:Int = 5

Global windowWidth:Int = 512;                '// window size
Global windowHeight:Int = 512;
Global textureWidth:Int = 512;               '// texture size
Global textureHeight:Int = 512;

Global maxTexSize:Int = 0 'maximum allowed size for 1D/2D texture

Global lightPos:Float[] = [ 140.0, 250.0, 140.0, 1.0];
Global ambientLight:Float[] = [ 0.2, 0.2, 0.2, 1.0];
Global diffuseLight:Float[] = [ 0.8, 0.8, 0.8, 1.0];
Global specularLight:Float[] = [ 0.8, 0.8, 0.8, 1.0];

Global lightRotation:Float = 0.0
Global texCoordOffsets:Float[18]
Global numPasses:Int = 1


Global camera:tCamera = New tCamera			
camera.setCamera(0,100,150,Pi/2,180)
Global posh:Float=0, posv:Float=0
Global oldMouseX:Int
Global oldMouseY:Int
Global elapsed:Long = 0
Global fps:Int = 0
Global Wireframe:Byte = 0

Global RENDERTEXTURE:Int[1] 'handle of texture we render the filter to
Global textureQuad:tTexturedQuad
Global drawFilter:Int = True
'end globals

mainloop() '################## START MAIN LOOP #####################
End

' Load shader from disk into a null-terminated string
Function LoadShaderText:Byte[](filename:String)
	Local fileLength:Int = FileSize(filename)
	Local shaderText:Byte[fileLength+1]
	Local shaderLength:Int = 0
	
	Local file:TStream = OpenFile( filename, True, False )
	If Not file RuntimeError "could not open file: "+filename

	
	While Not Eof(file)
		shaderText[shaderLength] = ReadByte(file)
		shaderLength:+1
		If shaderLength>fileLength Then shaderLength = shaderLength
	Wend
	CloseFile(file)
	
	If shaderLength > fileLength
		Print "todo"
	EndIf
	
	shaderText[shaderLength] = 0
	CloseFile( file )
	
	Return shaderText
End Function

function doCompileShader(shaderNum:int)
		Local success:Int = False
      glCompileShader(shaderNum);
      glGetShaderiv(shaderNum, GL_COMPILE_STATUS, Varptr success); 
        If ( Not success)
            printShaderInfoLog( shaderNum )
        Else
				print "shader compile success"
            printShaderInfoLog( shaderNum )
        EndIf
end function

function printShaderInfoLog(shader:int)
	local infologLen:int = 0;
	local charsWritten:int = 0;
	glGetShaderiv(shader, GL_INFO_LOG_LENGTH, varptr infologLen);
	
	if (infologLen > 0)
		Local infoLog:Byte[infologLen+1]
		if (infoLog = NULL)
			print "ERROR: Could not allocate infolog buffer"
			end;
		endif
		glGetShaderInfoLog(shader, infologLen, varptr charsWritten, infoLog);
		if infologLen > 1 print "InfoLog shader:"+String.FromCString(infoLog)
	endif
end function


' Compile shaders
Function PrepareShader(shaderNum:Int)
	Local fsString:Byte Ptr
	Local vsString:Byte Ptr
	
	Local fsStringPtr:Byte Ptr[1]
	Local vsStringPtr:Byte Ptr[1]
	Local success:Int = false
	
	fsString = LoadShaderText( shaderNames[shaderNum] + ".frag")
	vsString = LoadShaderText( shaderNames[shaderNum] + ".vert")
	If Not fsString or not vsString
		Print "unable to load "+ shaderNames[shaderNum]
		End
	EndIf
	
	print "Creating shader: " + shaderNames[shaderNum] 
	fShader[shaderNum] = glCreateShader(GL_FRAGMENT_SHADER);
	vShader[shaderNum] = glCreateShader(GL_VERTEX_SHADER);
	fsStringPtr[0] = fsString;
	vsStringPtr[0] = vsString;
	glShaderSource(fShader[shaderNum], 1, fsStringPtr, Null);
	glShaderSource(vShader[shaderNum], 1, vsStringPtr, Null);
	fsString = Null;
	vsString = Null;
	
	doCompileShader( fShader[shaderNum] )
	doCompileShader( vShader[shaderNum] )
	
	progObj[shaderNum] = glCreateProgram();
	glAttachShader(progObj[shaderNum], fShader[shaderNum]);
	if(shaderNames[shaderNum]="radialBlur" ) glAttachShader(progObj[shaderNum], vShader[shaderNum]);
	
	glLinkProgram(progObj[shaderNum]);
	glGetProgramiv(progObj[shaderNum], GL_LINK_STATUS, Varptr success);
	If ( Not success)
		Print "Shader Link Error:"
		printShaderInfoLog( progObj[shaderNum])
		End
	Else
		Print "Shader link success!"
		printShaderInfoLog( progObj[shaderNum])
	EndIf
	
	'// Program Object has changed, so we should revalidate
	needsValidation[shaderNum] = True;
End Function


Function DrawModels()
	Local lightPosEye:Float[3]
	Local mv:Float[16]
	
	' Transform light position to eye space
	glPushMatrix()
	glRotatef(lightRotation, 0.0, 1.0, 0.0)
	glGetFloatv(GL_MODELVIEW_MATRIX, mv)
	gltTransformPoint(lightPos, mv, lightPosEye) 'OpenGL superbible VectorMath.c
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos)
	glPopMatrix()
	
	' Draw a plane that the objects rest on
	glColor3f(0,0,0.9); 'blue
	glNormal3f(0.0, 1.0, 0.0)
	glBegin(GL_QUADS)
		glMultiTexCoord2f(GL_TEXTURE0, 0.0, 0.0);
		glVertex3f(-100.0, -25.0, -100.0);
		glMultiTexCoord2f(GL_TEXTURE0, 1.0, 0.0);
		glVertex3f(-100.0, -25.0, 100.0);
		glMultiTexCoord2f(GL_TEXTURE0, 1.0, 1.0);		
		glVertex3f(100.0,  -25.0, 100.0);
		glMultiTexCoord2f(GL_TEXTURE0, 1.0, 0.0);
		glVertex3f(100.0,  -25.0, -100.0);
	glEnd()
	
	' Draw red cube
	glColor3f(1.0, 0.0, 0.0)
	glutSolidCube(48.0)
	
	' Draw green sphere
	glColor3f(0.0, 1.0, 0.0)
	glPushMatrix()
	glTranslatef(-60, 0.0, 0.0)
	glScalef(25,25,25)
	Sphere() 'todo glutSolidSphere 25, 50, 50
	glPopMatrix()
	
	' Draw magenta torus
	glColor3f(1.0, 0.0, 1.0)
	glPushMatrix()
	glTranslatef(0.0, 0.0, 60.0)
	glutSolidTorus(8.0, 16.0, 50, 50)
	glPopMatrix()
	
	' Draw yellow cone
	glColor3f(1.0, 1.0, 0.0)
	glPushMatrix()
	glRotatef(-90.0, 1, 0, 0)
	glTranslatef(60.0, 0.0, -24.0)
	glutSolidCone(25.0, 50.0, 50, 50)
	glPopMatrix()
	
	' Draw cyan teapot
	glColor3f(0.0, 1.0, 1.0)
	glPushMatrix()
	
	glTranslatef(0.0, 20.0, -60.0)
	glRotatef(-90.0, 1, 0, 0)
	'glutSolidTeapot(25.0)
	'glScalef(15,15,15)
	'teapot(25.0, 0,0)
	glutSolidCone(25.0, 50.0, 50, 50)
	glPopMatrix()
	
	' Go away
	glColor4f(1.0, 1.0, 1.0, 1.0)
	glDisable(GL_LIGHTING)
	glPushMatrix()
		
		glTranslatef(0.0, 100.0, -150.0)	
		glRotatef(180, 0,1,0)
		glRotatef(90, 1,0,0)
		
		textureQuad.draw()
	glPopMatrix()
	glEnable(GL_LIGHTING)

End Function

Function RenderScene()
	Local pass:Int = 0
	
	' Track camera angle
	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	gluPerspective(45.0, 1.0, 1.0, 1000.0)
	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity()
	gluLookAt( camera.vPosition._v[0], camera.vPosition._v[1], camera.vPosition._v[2],..
			camera.vView._v[0], camera.vView._v[1], camera.vView._v[2],..
			camera.vUpVector._v[0], camera.vUpVector._v[1], camera.vUpVector._v[2] )
	
	glClearColor(0.0, 0.0, 0.0, 1.0)
	
	' clear the window
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
	
	' Validate our shader before first use
	If needsValidation[whichShader]
		Local success:Int = False
		
		glValidateProgram(progObj[whichShader])
		glGetProgramiv(progObj[whichShader], GL_OBJECT_VALIDATE_STATUS_ARB, Varptr success);
		
		If Not success
			printShaderInfoLog(progObj[whichShader])
		EndIf
		
		needsValidation[whichShader] = False
	EndIf
	
	If Wireframe
		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE )
	Else
		glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ) 'wireframe
	EndIf

	'Draw objects in the scene
	DrawModels()
	
	If drawFilter
	' Redraw texture w/fragment shader
	If useHighLevel
		glUseProgram(progObj[whichShader]);
		
		Local uniformLoc:Int
		uniformLoc = glGetUniformLocation(progObj[whichShader], "sampler0");
		If( uniformLoc <> -1 )
			glUniform1iARB(uniformLoc, 0);
		Else
			'Print "error. could not find uniformLoc for variable sampler0"
		EndIf		
		
		uniformLoc = glGetUniformLocation(progObj[whichShader], "tc_offset" )
		If( uniformLoc <> -1 )
			glUniform2fv( uniformLoc, 9, texCoordOffsets )
		Else
			'Print "error. could not find uniformLoc for variable tc_offset"
		EndIf
	End If

	glDisable(GL_DEPTH_TEST);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glColor3ub(0,0,255)
	glBindTexture(GL_TEXTURE_2D, RENDERTEXTURE[0])
    For pass = 0 Until numPasses
        '// Copy original scene To texture
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (windowWidth-textureWidth)/2, (windowHeight-textureHeight)/2, textureWidth, textureHeight, 0);


        glClear(GL_COLOR_BUFFER_BIT);
        glBegin(GL_QUADS);
            glMultiTexCoord2f(GL_TEXTURE0, 0.0, 0.0);
            glVertex2f(-(textureWidth / Float(windowWidth)), -(textureHeight / Float(windowHeight)));
            glMultiTexCoord2f(GL_TEXTURE0, 0.0, 1.0);
            glVertex2f(-(textureWidth / Float(windowWidth)), (textureHeight / Float(windowHeight)));
            glMultiTexCoord2f(GL_TEXTURE0, 1.0, 1.0);
            glVertex2f((textureWidth / Float(windowWidth)), (textureHeight / Float(windowHeight)));
            glMultiTexCoord2f(GL_TEXTURE0, 1.0, 0.0);
            glVertex2f((textureWidth / Float(windowWidth)), -(textureHeight / Float(windowHeight)));
        glEnd();
    Next

	glEnable(GL_DEPTH_TEST);
	

	If useHighLevel
		glUseProgram(0);
	EndIf
	
	EndIf 'drawFilter 
	
	?debug
	Local errorCode:Int = glGetError()
	If errorCode = GL_NO_ERROR
		'all ok
	Else
		Print "GL ERROR!"
		If errorCode = GL_INVALID_ENUM Print "invalid enum"
		If errorCode = GL_INVALID_VALUE Print "invalid value"
		If errorCode = GL_INVALID_OPERATION Print "invalid operation"
		If errorCode = GL_STACK_OVERFLOW Print "stack overflow"
		If errorCode = GL_STACK_UNDERFLOW Print "stack underflow"
		If errorCode = GL_OUT_OF_MEMORY Print "GL_OUT_OF_MEMORY"
		If errorCode = GL_TABLE_TOO_LARGE Print "GL_TABLE_TOO_LARGE"
	EndIf
	?
	
	glColor3ub(255,255,255)
	glDisable(GL_LIGHTING);
	GLDrawText("hold left mouse to draw camera, use WASD to move",20,0)
	GLDrawText("hold right mouse for wireframe",20,15)
	GLDrawText("active shader program:"+shaderNames[whichShader], 20, 30)
	glColor3ub(255,255,0)
	GLDrawText("Press 1-7 to change whichshader = "+whichshader, 20, 45)
	glColor3ub(255,255,255)
	GLDrawText("Press PG_UP/PG_DOWN to rotate light0", 20, 60)
	GLDrawText("Press arrow up/down to change numPasses = "+numPasses, 20, 75)
	GLDrawText("fps:"+fps,20,90)
	glEnable(GL_LIGHTING);
	Flip
End Function

Function SetupRC()	
	If GL_fragment_shader And GL_vertex_shader And GL_ARB_shading_language_100
		highLevelAvailable = True
		useHighLevel = True
	EndIf
	
	If Not highLevelAvailable
		Print "this program only supports highlevel shaders, GLSL... so its quitting now"
		End
	EndIf
	
	' Make sure we have multitexture and cube maps!
	If GL_VERSION_1_3
		Print "gl version 1.3 or higher. good to go."
	Else
		Print "OpenGL 1.3 is not available. Quitting"
		End
	EndIf
	
	glGetIntegerv(GL_MAX_TEXTURE_SIZE, Varptr maxTexSize);
	Print "max tex size:"+maxTexSize
	
	
	glClearColor(0.5, 0.5, 0.5, 0.0)	' Color to clear the screen with
	glClearDepth 1.0										' The depth to clear the Zbuffer to (0.0=near clip plane, 1.0=far clip plane)
	glDepthFunc(GL_LESS)								' Only draw pixels if the fragment/pixel Z depth, is LESS than the Zbuffer value
	glEnable(GL_DEPTH_TEST)							' Enable depth testing (Z sorting of pixels)
	glFrontFace(GL_CW)									' Draw polys facing viewer Clockwise
	glShadeModel(GL_SMOOTH)	
	glViewport(0,0,800,600)
	
	glMatrixMode(GL_PROJECTION)				' The Matrix to which subsequent Matrix operations work on
	glLoadIdentity()
	Local aspect# = Float(800)/Float(600)
	gluPerspective(45.0,aspect#,1.0,100.0) ' y-FoV#, Aspect#, Near#, Far#
	
	' Misc. state
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_LIGHTING);
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_NORMALIZE);
	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);	
	glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);	
	
	'Texture state
	glActiveTexture(GL_TEXTURE0);
	glGenTextures(1, RENDERTEXTURE)
	glBindTexture(GL_TEXTURE_2D, RENDERTEXTURE[0]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	
	' Load and compile shaders
	For Local i:Int = 0 Until TOTAL_SHADERS
		PrepareShader(i)
	Next
End Function

Function ChangeSize(w:Int, h:Int)
    Local i:Int, j:Int;

    windowWidth = w;
    windowHeight = h;
    glViewport(0, 0, windowWidth, windowHeight);

    '// find largest power-of-two texture smaller than window
	textureWidth = maxTexSize
	textureHeight = maxTexSize
    While (textureWidth > windowWidth)
        textureWidth = textureWidth / 2;
    Wend

    While (textureHeight > windowHeight)
        textureHeight = textureHeight / 2;
    Wend
    textureWidth :*2 'not quite correct?
    textureHeight :* 2

    Local xInc:Float = 1.0/ Float(textureWidth);
    Local yInc:Float = 1.0/ Float(textureHeight);

    For i = 0 To 2 
        For j=0 To 2
            texCoordOffsets[(((i*3)+j)*2)+0] = (-1.0 * xInc) + ( Float(i) * xInc);
            texCoordOffsets[(((i*3)+j)*2)+1] = (-1.0 * yInc) + ( Float(j) * yInc);
        Next
    Next

	Print "texsize:" + textureWidth +"x"+textureHeight
	Print "window:" + windowWidth +"x"+windowHeight 
End Function

Function mainloop()
	SetGraphicsDriver GLGraphicsDriver()
	GLGraphics 800,600,0,0,GRAPHICS_BACKBUFFER|GRAPHICS_DEPTHBUFFER 
	glewInit() 'must be done AFTER window creation
	
	
	initCamera()
	SetupRC()
	ChangeSize(GraphicsWidth(), GraphicsHeight() )
	textureQuad= New tTexturedQuad
	
	Local frames:Int = 0
	Local frameTime:Long = 0
	
	While Not KeyDown(KEY_ESCAPE) And Not AppTerminate()
		Local time:Long = MilliSecs()
		Wireframe=MouseDown(2)
		RenderScene()
		
		elapsed = MilliSecs() - time
		frames:+1
		frameTime :+ elapsed
		If(frameTime > 1000)
			fps = frames
			frames = 0
			frameTime = 0
		EndIf
		Keys() 'poll keys for camera movement
	Wend
	
	If glDeleteProgram
		For Local i:Int = 0 Until TOTAL_SHADERS
			glDeleteProgram( progObj[i] )
			glDeleteShader( fShader[i] )
			glDeleteShader( vShader[i] )
		Next
	EndIf
	
	End

End Function

Function keys()
	If KeyHit(KEY_SPACE) drawFilter = Not drawFilter 
	
	For Local nums:Int = 0 To TOTAL_SHADERS
	 If KeyDown(49+nums) Then whichshader = nums
	Next

	If KeyDown(KEY_PAGEUP) lightRotation :- 0.1
	If KeyDown(KEY_PAGEDOWN) lightRotation :+ 0.1
	
	If KeyHit(KEY_UP) numPasses:+1
	If KeyHit(KEY_DOWN) numPasses:-1
	If numPasses > 4 numPasses = 4
	If numPasses < 1 numPasses = 1
	
	Local inputL:Int =0
	Local inputR:Int =0
	Local inputU:Int =0
	Local inputD:Int =0
	
	If KeyDown(KEY_A) Then inputL=1
	If KeyDown(KEY_D) Then inputR=1
	If KeyDown(KEY_W) Then inputU=1
	If KeyDown(KEY_S) Then inputD=1
	
	
	Local vx:Int = inputR-inputL
	Local vy:Int = 0 ' up down
	Local vz:Int = inputU-inputD 'fwd bkwd
	'camera.moveCamera( vz * 0.01 )
	camera.moveCam(vz*1)
	camera.slideCam( -vx, 0 )
	
	Local mouseSpeedX:Float = (Float(MouseX()) -Float(oldMouseX)) / elapsed * 0.01
	Local mouseSpeedY:Float = (Float(MouseY()) -Float(oldMouseY)) / elapsed * 0.01
	oldMouseX = MouseX()
	oldMouseY = MouseY()
	If MouseDown(1)
		
		MoveMouse( oldMouseX, oldMouseY )
		posh = mouseSpeedX
		posv = mouseSpeedY
		camera.rotateCamera( posh, posv )
		posh=0
		posv=0
	EndIf
End Function

'// Transform a point by a 4x4 matrix
Function gltTransformPoint( vSrcVector:Float[], mMatrix:Float[], vOut:Float[] )
	vOut[0] = mMatrix[0] * vSrcVector[0] + mMatrix[4] * vSrcVector[1] + mMatrix[8] *  vSrcVector[2] + mMatrix[12];
	vOut[1] = mMatrix[1] * vSrcVector[0] + mMatrix[5] * vSrcVector[1] + mMatrix[9] *  vSrcVector[2] + mMatrix[13];
	vOut[2] = mMatrix[2] * vSrcVector[0] + mMatrix[6] * vSrcVector[1] + mMatrix[10] * vSrcVector[2] + mMatrix[14];    
End Function

Function initCamera()
	MoveMouse(GraphicsWidth()/2, GraphicsHeight()/2)
End Function

Type tCamera
	Field vPosition:Vec3d = New Vec3d
	Field vView:Vec3d = New Vec3d
	Field vUpVector:Vec3d = New Vec3d
	
	Field radius:Float = 1
	Field moveDist:Float = 1
	
	Field hRadians:Float
	Field vRadians:Float
	
	Method setCamera(x#,y#,z#,h#,v#)
		vPosition.set(x,y,z)
		vView.set(0,0,0)
		vUpVector.set(0,0,0)
		hRadians = h
		vRadians = v
		
		rotateCamera(0,0)
	End Method
	
	Method rotateCamera(h:Float, v:Float)
		hRadians :+ h
		vRadians :+ v
		
		vView._v[1] = vPosition._v[1] + radius * sinRad(vRadians)	
		vView._v[0] = vPosition._v[0] + (radius * cosRad(vRadians)*cosRad(hRadians))	
		vView._v[2] = vPosition._v[2] + (radius * cosRad(vRadians)*sinRad(hRadians))
		
		vUpVector._v[0] = vPosition._v[0] - vView._v[0]
		vUpVector._v[1] = Abs(vPosition._v[1] + (radius*sinRad(vRadians+Pi/2)))
		vUpVector._v[2] = vPosition._v[2] - vView._v[2]
	End Method
	
	Method moveCam(d:Float)
		vPosition._v[1] :+ d*moveDist*sinRad(vRadians)
		vPosition._v[0] :+ d*moveDist*cosRad(vRadians)*cosRad(hRadians)
		vPosition._v[2] :+ d*moveDist*cosRad(vRadians)*sinRad(hRadians)
		rotateCamera(0,0)
	End Method
	
	Method slideCam(h:Float, v:Float)
		vPosition._v[1] :+ v*moveDist
		vPosition._v[0] :+ h*moveDist*cosRad(hRadians+Pi/2)
		vPosition._v[2] :+ h*moveDist*sinRad(hRadians+Pi/2)
		rotateCamera(0,0)		
	End Method
End Type

Type Vec3d
	'"private"
	Field _v:Double[3]
	
	Method New()
		_v[0] = 0; _v[1] = 0; _v[2] = 0;
	End Method
	
	Method set(x:Double, y:Double, z:Double)
		_v[0] = x; _v[1] = y; _v[2] = z
	End Method
End Type

Function sinRad:Float(r:Float)
	Return Sin(r*180/Pi)
End Function

Function cosRad:Float(r:Float)
	Return Cos(r*180/Pi)
End Function

Function radToDeg:Float(r#)
	Return r*180/Pi
End Function

' Taken from the glEarth.kc example/demo that comes with Ken Silvermans Evaldraw
'nu = 32; du = PI*2/nu;
'nv = 16; dv = PI/nv;
'for(v=PI,iv=0;iv<=nv;iv++,v+=dv)
'{
'   glBegin(GL_TRIANGLE_STRIP);
'   for(u=0,iu=0;iu<=nu;iu++,u+=du)
'   {
'      glTexCoord(u*.5/PI,v/PI);
'      glVertex(sin(v)*cos(u),cos(v),sin(v)*sin(u));
'      glTexCoord(u*.5/PI,(v-dv)/PI);
'      glVertex(sin(v-dv)*cos(u),cos(v-dv),sin(v-dv)*sin(u));
'   }
'}
Function sphere() 'glutSolidSphere takes radius, slices, stacks
	Local nu# = 32; Local du# = Pi*2/nu;
	Local nv# = 16; Local dv# = Pi/nv;
	Local v# = Pi;

	for local iv:int = 0 to nv
		
		Local u#=0;
		glBegin(GL_TRIANGLE_STRIP);
		for local iu:int = 0 to nu
			local x1# = Sin( radToDeg(v) )*Cos( radToDeg(u) )
			local y1# = Cos(radToDeg(v))
			local z1# = Sin(radToDeg(v))*Sin(radToDeg(u))
			
			local x2# = Sin(radToDeg(v-dv))*Cos(radToDeg(u))
			local y2# = Cos(radToDeg(v-dv))
			local z2# = Sin(radToDeg(v-dv))*Sin(radToDeg(u))

			local yzzy# = y1*z2-z1*y2
			local zxxz# = z1*x2-x1*z2
			local xyyx# = x1*y2-y1*x2
			glNormal3f(yzzy,zxxz,xyyx);
			
			glTexCoord2f(u*.5/Pi,v/Pi);
			glVertex3f(x1,y1,z1)
			glTexCoord2f(u*.5/Pi,(v-dv)/Pi);
			glVertex3f(x2,y2,z2);
			
			u:+du;
		next
		glEnd()
		v:+dv
	next
	
End Function


Function drawBox(size:Float, drawStyle:Int )

  Local n:Float[][] =[.. '6*3
    [-1.0, 0.0, 0.0],..
    [0.0, 1.0, 0.0],..
    [1.0, 0.0, 0.0],..
    [0.0, -1.0, 0.0],..
    [0.0, 0.0, 1.0],..
    [0.0, 0.0, -1.0]..
  ];


  Local faces:Float[][] =[.. '6*4
    [0.0, 1.0, 2.0, 3.0],..
    [3.0, 2.0, 6.0, 7.0],..
    [7.0, 6.0, 5.0, 4.0],..
    [4.0, 5.0, 1.0, 0.0],..
    [5.0, 6.0, 2.0, 1.0],..
    [7.0, 4.0, 0.0, 3.0]..
  ];

  Local v:Float[8,3] '8*3
	v[0,0] = -size / 2;
	v[1,0] = -size / 2;
	v[2,0] = -size / 2;
	v[3,0] = -size / 2;
	
	v[4,0] = size / 2;
	v[5,0] = size / 2;
	v[6,0] = size / 2;
	v[7,0] = size / 2;
	
	v[0,1] = -size / 2;
	v[1,1] = -size / 2;
	v[4,1] = -size / 2;
	v[5,1] = -size / 2;
	
	v[7,1] = size / 2;
	v[3,1] = size / 2;
	v[2,1] = size / 2;
	v[6,1] = size / 2;
	
	v[7,2] = -size / 2;
	v[4,2] = -size / 2;
	v[0,2] = -size / 2;
	v[3,2] = -size / 2;
	
	v[6,2] = size / 2;
	v[5,2] = size / 2;
	v[1,2] = size / 2;
	v[2,2] = size / 2;

  For Local i:Int = 5 To 0 Step -1
    glBegin(drawStyle);
    glNormal3fv(Varptr n[i][0] );
    glVertex3fv(Varptr v[faces[i][0],0]);
    glVertex3fv(Varptr v[faces[i][1],0]);
    glVertex3fv(Varptr v[faces[i][2],0]);
    glVertex3fv(Varptr v[faces[i][3],0]);
    glEnd();
  Next
End Function

Function glutSolidCube(size:Double)
	drawBox(size, GL_QUADS)
End Function

Function glutSolidTorus(innerRadius:Float, outerRadius:Float, nsides:Int, rings:Int)
	doughnut(innerRadius, outerRadius, nsides, rings)
End Function

Function doughnut(r:Float, _r:Float, nsides:Int, rings:Int)
  Local i:Int, j:Int;
  Local theta:Float, phi:Float, theta1:Float;
  Local cosTheta:Float, sinTheta:Float;
  Local cosTheta1:Float, sinTheta1:Float;
  Local ringDelta:Float, sideDelta:Float;

  ringDelta = 2.0 * 180 / rings; 'changed from PI to 180degrees because bmax uses degrees in cos/sin
  sideDelta = 2.0 * 180 / nsides;

  theta = 0.0;
  cosTheta = 1.0;
  sinTheta = 0.0;
  For i = rings-1 To 0 Step -1
    theta1 = theta + ringDelta;
    cosTheta1 = Cos(theta1);
    sinTheta1 = Sin(theta1);
    glBegin(GL_QUAD_STRIP);
    phi = 0.0;
    For j = nsides To 0 Step -1
      Local cosPhi:Float, sinPhi:Float, dist:Float;

      phi :+ sideDelta;
      cosPhi = Cos(phi);
      sinPhi = Sin(phi);
      dist = _R + r * cosPhi;

      glNormal3f(cosTheta1 * cosPhi, -sinTheta1 * cosPhi, sinPhi);
      glVertex3f(cosTheta1 * dist, -sinTheta1 * dist, r * sinPhi);
      glNormal3f(cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi);
      glVertex3f(cosTheta * dist, -sinTheta * dist,  r * sinPhi);
    Next
    glEnd();
    theta = theta1;
    cosTheta = cosTheta1;
    sinTheta = sinTheta1;
  Next
End Function

Function glutSolidCone(base:Double, height:Double, slices:Int, stacks:Int)
  QUAD_OBJ_INIT();
  gluQuadricDrawStyle(quadObj, GL_FILL);
  gluQuadricNormals(quadObj, GL_SMOOTH);
  ' If we ever changed/used the texture Or orientation state
  '   of quadObj, we'd need to change it to the defaults here
  '   with gluQuadricTexture And/Or gluQuadricOrientation. */
  gluCylinder(quadObj, base, 0.0, height, slices, stacks);
End Function

Function QUAD_OBJ_INIT()
	If Not quadObj Then initQuadObj()
End Function

Function initQuadObj()
  quadObj = gluNewQuadric();
  If (Not quadObj) Then RuntimeError("Could not make quad obj. out-of-memory")
End Function


Type tTexturedQuad
	Field texture:Int[1]
	
	Method New()
		loadTexture()
	End Method
	
	Method loadTexture()
		Local image:TPixmap = LoadPixmap("texture.jpg")
		If image <> Null
			Print "texture load success"
		Else 
			Print "texture could not be loaded"
			Print "quitting..."
			End
		EndIf
		
		glGenTextures(1, texture) 'generate tex name for 1
		glBindTexture(GL_TEXTURE_2D, texture[0])
		
		'actually generate texture:
		'expects png or jpg, not bmp
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width, image.height, 0, GL_RGB, GL_UNSIGNED_BYTE, image.pixels)	
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 'linear filtering
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) 'linear filtering
		
		image = Null 'loaded into opengl now, so free it
	End Method
	
	Method draw()
		glEnable(GL_TEXTURE_2D)
		glBindTexture(GL_TEXTURE_2D, texture[0])
		glNormal3f(0.0, 1.0, 0.0)
			glBegin(GL_QUADS)
				glTexCoord2f(1.0, 0.0);
				glVertex3f(-100.0, 0.0, -100.0);
				glTexCoord2f( 1.0, 1.0);
				glVertex3f(-100.0, 0.0, 100.0);
				glTexCoord2f(0.0, 1.0);		
				glVertex3f(100.0,  0.0, 100.0);
				glTexCoord2f(0.0, 0.0);
				glVertex3f(100.0,  0.0, -100.0);
			glEnd()
		glDisable(GL_TEXTURE_2D)
	End Method
End Type


